TUIアプリケーションフレームワーク asciimatics を使ってみよう
asciimatics について
https://gyazo.com/04a172eae76684d06741f62853b86156
次のような機能を提供しています。
色とスタイルを適用したテキストを出力
カーソル位置の制御
キーボード入力(ブロックまたはエコーなし)
マウス入力
コンソールのサイズ変更時の検出と処理
スクリーンスクレイピング
アンチエイリアスASCII線画
画像(JPEGおよびGIF形式)からASCIIへの変換
多くのアニメーション効果
スプライト、パーティクルシステム、バナーなど。
TUI用のさまざまなウィジェット
ボタン、テキストボックス、ラジオボタン、DatePicker(日付指定)、TimePicker(時間指定)など。
インストール
asciimatics は拡張モジュールなのでインストールが必要です。
code: zsh
% pip install asciimatics
Screenクラス
まず、Screenクラスから説明します。Screenクラスのwrapper()メソッドを呼び出します。
これにより、実行に必要なすべての初期化が処理され、作成された画面が指定された関数に渡されます。
code: asciimatics_screen.py
from asciimatics.screen import Screen
from time import sleep
def demo(screen):
screen.print_at('Hello world!', 0, 0)
screen.refresh()
sleep(10)
Screen.wrapper(demo)
このコードと同じことは、@ManagedScreenで関数をデコレートしても実装できます。
code: asciimatics_managedscreen.py
from asciimatics.screen import Screen
from asciimatics.screen import ManagedScreen
from time import sleep
@ManagedScreen
def demo(screen=None):
screen.print_at('Hello world!', 0, 0)
screen.refresh()
sleep(10)
demo()
あるいは、ManagedScreenはコンテキストマネージャとして使用することもできます。
code: asciimatics_managedscreen_contextmanager.py
from asciimatics.screen import Screen
from asciimatics.screen import ManagedScreen
from time import sleep
def demo():
with ManagedScreen() as screen:
screen.print_at('Hello world!', 0, 0)
screen.refresh()
sleep(10)
demo()
出力
Screenを作成したあとは、clear() で画面をクリアしましょう。
clear()メソッドでちらつきが表示される場合は、clear_buffer() はを使用するようにしてください。
テキストを出力する最も簡単な方法は、print_at()メソッドを使用することです。 これにより、文字列を目的の場所に指定した色で配置できます。 座標は画面の左上から始まり、上下に移動するゼロインデックスであるため、前述の例は画面の左上である(0,0)に "Hello world!" を表示します。
色
ターミナルには長い歴史があり、これは色に関しては明らかです。 古くはの端末の色は限られていたため、太字、下線、反転ビデオなどの効果を属性を使用して変更しました。 昨今では、より多くの色が追加され、いくつかの端末ではフル24ビットカラー(True Color)を設定することができます。
現時点では、asciimatics は最大256色のパレットに制限しています。 色のプロパティを確認すると、端末がサポートする色の数を確認できます。現在では、ほとんどの端末が最低8色をサポートします。 これらは、ScreenクラスのCOLOUR_xxx定数によって定義されています。
asciimatics.constantsに定義されています。
code: python
COLOUR_BLACK = 0
COLOUR_RED = 1
COLOUR_GREEN = 2
COLOUR_YELLOW = 3
COLOUR_BLUE = 4
COLOUR_MAGENTA = 5
COLOUR_CYAN = 6
COLOUR_WHITE = 7
これらは、背景色と前景色に適用することができます。多くのシステムでは、属性を使用して、前景色の数を2倍にすることもできます。
16色を超える表示が可能な端末(term-256colorなど)では、代わりに端末の色のインデックスを直接使用することができます。 色の完全なリストについては、256色テストパターン をご覧ください。 これらの追加の色を使用する場合は、8つの一般的な色のみを使って減色モードを使用するようにします。 これを行う方法の例については、Rainbowクラスを参照してください。
属性
属性は、初期のハードウェア端末が色を付ける前にサポートしていたいくつかの基本的な方法で、表示されるテキストを変更する方法です。 ほとんどのシステムはハードウェア端末を使用しなくなりましたが、この概念はすべてのネイティブコンソールAPIで維持されるため、ここでも使用されます。
サポートされる属性は、ScreenクラスのA_xxx定数によって定義されます。 完全なリストは次のとおりです。
asciimatics.constantsに定義されています。
code: python
A_BOLD = 1
A_NORMAL = 2
A_REVERSE = 3
A_UNDERLINE = 4
ほとんどのシステムは、太字(bold)、通常(nomarl)、およびリバース(reverse)の属性をサポートします。 他にもいくつかの設定を行うことができますが、クロスプラットフォームでの使用が難しくなるため、非推奨になります。 この属性は、print_at()へ与えることができます。
code: python
screen.print_at('Hello world!', 0, 0, COLOUR_GREEN, A_BOLD)
マルチカラー
より複雑なことをしたい場合は、paint()メソッドを使用して、表示する各文字のカラーマップを指定できます。 カラーマップは、表示されるテキストと同じ長さの色/属性値(タプルまたはリスト)のリストである必要があります。 この方法は通常、レンダラーからの複雑なマルチカラーテキストを表示するために使用されます。 詳細については、Animationクラスを参照してください。
Unicode
asciimartics は Unicode 文字の表示をサポートしています。当然ながら日本語も表示することができます。プラットフォームによっては表示できないときは、こちら を参照してみてください。 code: asciimatics_unicode.py
from asciimatics.constants import COLOUR_GREEN, A_BOLD
from asciimatics.screen import ManagedScreen
from time import sleep
def demo():
with ManagedScreen() as screen:
screen.print_at('☎ 電話番号:', 0, 0, COLOUR_GREEN, A_BOLD)
screen.refresh()
sleep(10)
demo()
画面のリフレッシュ
Screenクラスは、表示される内容のバッファーを維持し、refresh()メソッドが呼び出されたときにのみ実際に表示します。 これは、新しいコンテンツが作成されるときの端末のちらつきを減らすために行われます。
アプリケーションは、表示する必要があるすべてのものを再レンダリングし、すべての新しいコンテンツの準備ができたら更新を呼び出す必要があります。 play()メソッドとdraw_next_frame()メソッドは、各フレームの最後に自動でこの処理を行うため、アニメーションで再度呼び出す必要はありません。
入力
ユーザーから入力を受け取るためには、get_event()メソッドを使用します。 これは、リターンキーを待たずに、また画面にエコーすることなく(キーボードイベントの場合)、最新のキー押下やマウスイベントが即座に返されます。 使用可能なイベントがない場合は、None が返されます。
返されるクラスは、イベントによって異なり、KeyboardEventもしくはMouseEventのいずれかになります。 それぞれの取り扱いについては、以下で説明します。
入力が利用可能になるまで待機したいときは、wait_for_input()メソッドを使用して実行をブロックしてから、get_event()を呼び出して入力を取得できます。
KeyboardEvent
KeyboardEventは、キーが押されたときの自動リピートを含む、すべてのキーが押されたときに発生します。key_code は、可能であればキーコード(キーボードの状態(Caps Lockなどを考慮))、またはそうでない場合は拡張キーコード(ScreenクラスのKEY_xxx定数)です。
たとえば、キーaを押すと、通常、get_event()はkey_code 97 の KeyboardEvent を返します。これは ord('a')です。 Caps Lockをオンにして同じキーを押すと、65になります。これはord('A')です。 ファンクションキーF7を押すと、CapsLockに関係なく常にKEY_F7が返されます。
キーボードのコントロールキー(CTRL)は、制御コード(ASCIIテーブルの最初の31コード)を返します。 ctrl()メソッドを使用して、任意のキーの制御コードを計算することができます。すべてのシステムがすべてのキーの制御コードを返すわけではないので、asciimaticsがキーが機能すると認識しいない場合、この関数はNoneを返す可能性があることに注意してください。システムとの互換性を最大限に高めるには、アルファベット文字の制御コード(AからZ)を使用してください。
V1.7以降、ASCII文字セット以外のUnicode文字のキーボードイベントを取得することもできます。これらは、以前のASCII文字のサポートと同様に、Unicode文字の序数表現も返します。
代わりにランダムなゴミが表示されている場合は、システムがUnicode用に正しく構成されていない可能性があります。これを修正する方法については、Unicode文字が機能しないを参照してください。
MouseEvent
MouseEventイベントは、マウスの動きやボタンのクリックによって発生します。 画面上のマウスの現在の座標は、xプロパティとyプロパティに保存されます。 ボタンがクリックされた場合、これはbuttonsプロパティによって知ることができます。ボタンには、LEFT_CLICK、RIGHT_CLICK、およびDOUBLE_CLICK のいずれかになります。
画面のリサイズ
プログラムで画面サイズを変更することはできません。ただし、ユーザーは、プログラムの実行中に端末またはコンソールのサイズを変更できます。 Asciimaticsは、元のサイズ内で可能な限り最高の状態で動作し続けるか、必要に応じて画面を新しいサイズに再作成するように指示できます。
もう少し詳しくは、dimensionsプロパティから(作成時の)画面サイズを読み取ることができます。ユーザーが任意の時点でサイズを変更した場合は、has_resized()メソッドを呼び出すことでこれを検出できます。さらに、stop_on_resize=True を指定することで、シーンの再生中に例外が発生した場合に例外をスローするように画面に指示できます。
上記のオプションのいずれかを使用して画面サイズが変更されたことを検出したら、現在の画面を続行するか、破棄して新しい画面を作成するかを決定できます(新しいScreenオブジェクトを作成するだけです)。後者を行う場合、通常、新しい境界内で実行するには、関連するシーンとエフェクトを再作成する必要があります。これを処理する方法のサンプルとして、bars.pyデモを参照してください。
スクリーンスクレイピング
画面の特定の場所にすでに表示されているものを読み取れると便利な場合があります。 これは、スクリーンスクレイピングと呼ばれることがあります。 これは、get_from()メソッドを使用して実行できます。 画面上の任意の1文字の場所について、表示されている文字と属性を(4タプルとして)返します。
code: python
current_char, fg, attr, bg = screen.get_from(x, y)
if current_char != 32:
screen.print_at('X', x, y)
シェイプ
Screenオブジェクトは、ASCII文字を使用したラインや、アンチエイリアス処理されたラインの描画機能も提供します。 move()メソッドは描画カーソルを指定された座標に移動し、draw()メソッドは現在のカーソル位置から指定された座標に直線を描画します。
charパラメータを使用してアンチエイリアスを上書きすることができます。 これは、すでに描画されているものをクリアしようとするときに最も役立ちます。
code: python
# 画面の左上から対角線を引きます
screen.move(0, 0)
screen.draw(10, 10)
# 対角線をクリア
screen.move(0, 0)
screen.draw(10, 10, char=' ')
ラインが太すぎる場合は、thin=True を与えると、細いラインになります。両方のスタイルの例は、Clockサンプルコードにあります。
さらに、必要な形状を定義するために渡された一連のポイントを使用して、指定された色で塗りつぶされたポリゴンを描画する fill_polygon()メソッドがあります。 これはスキャンラインアルゴリズムを使用するため、あるポリゴンを別のポリゴンの内側に定義することで、シェイプの内側に穴を開けることができます。
code: python
# 中央に小さな長方形の穴がある大きな穴を描きます
この描画方法はUnicodeブロック文字に対応しており、デフォルトで端末に適した文字セットになります。可能な場合はUnicodeブロック文字を使用し、そうでない場合は純粋なASCIIテキストにフォールバックします。
レンダリング
アニメーションを作成する場合、一連のマルチカラーテキスト画像が必要です。 ここで、Rendererオブジェクトを使用します。
Rendererオブジェクトは、paint()メソッドを使用した表示に適した形式で1つ以上のテキスト文字列と関連するカラーマップを返すオブジェクトです。 このテキスト文字列とカラーマップの照合は、レンダリングテキストと呼ばれます。 複雑さは、ひとつのモノクロ文字列から、カラーまたはアニメーションGIFのASCII表現のフレームまでさまざまです。
すべてのRendererクラスは抽象クラスのAPIを実装する必要がありますが、2つの基本的なものがあります。
StaticRenderer
StaticRendererは、事前にレンダリングされたレンダリングテキストのシーケンスを作成します。 それらは、事前に処理できる静的コンテンツで初期化されます。
code: python
# Pre-render ASCIIMATICS using the big Figlet font
renderer = FigletText("ASCIIMATICS", font='big')
DynamicRenderer
DynamicRendererは、レンダリングテキストをオンデマンドで作成します。 これらは、レンダリング時のプログラムまたは画面の状態に依存します。
code: python
# Render a bar chart with random bars formed of equals signs.
def fn():
return randint(0, 40)
renderer = BarChart(10, 40, fn, fn, char='=') レンダラーを取得したら、rendered_text()を呼び出すことにより、表示される次のテキストを抽出できます。 これにより、静的なレンダリングテキストが順番にループして表示するか、新しい動的にレンダリングテキストが作成されて返されます(Screen.paint()メソッドで使用するため)。
これを画面で直接行うのではなく、エフェクトを使用してこれを処理するようにします。
詳細については、アニメーションを参照してください。
asciimaticsによって提供される多くの組み込みレンダラーがあります。 次のセクションでは、エリアごとにそれぞれを簡単に説明します。 レンダラーのその他の例については、asciimaticsサンプルフォルダーを参照してください。
画像をASCIIテキスト
Asciimaticsは、JPEG、GIFなど画像ファイルを同等のテキストに変換することができます。
これには、2つの方法が提供されています。
ImageFile:画像をグレースケールテキストに変換します。
ColourImageFile:画像をフルカラーテキストに変換します
すべての画面のパレットを使用します。
どちらもアニメーションGIFをサポートしており、描画されると各画像をループして表示します。
アニメーションオブジェクト
Asciimaticsは、より複雑なアニメーション効果のために次のレンダラーを提供しています。
BarChart:一連のデータの水平棒グラフを描画します。
基本的に動的である可能性があります
Fire:燃える火をシミュレートします。
Plasma:ニメーション化された「プラズマ」をシミュレートします
Kaleidoscope:万華鏡をシミュレートします。
テキストと色の操作
次のレンダラーは、いくつかの簡単なテキストと色の操作を提供しています。
FigletText:大きなFIGletテキストを描画します
Rainbow:指定されたレンダラーをRainbowとして再度色付けします
RotatedDuplicate:指定されたレンダラーの回転された複製を作成します。
ボックス
次のレンダラーは、いくつかの単純なボックスとボックス化されたテキストを提供します。
Box:単純なボックスを描画します。
SpeechBubble:指定されたテキストの周りに吹き出しを描画します。
静的な色付けテキスト
静的にレンダリングされた出力を作成するときは、テキストの残りの部分とインラインで色を定義すると便利です。 StaticRendererクラスは、エスケープシーケンス${n1,n2,n3...}をサポートします。ここで、nX は数字を与えます。
このシーケンスはエスケープシーケンス ${c,a,b} として定義され、現在のカラータプルを前景色)(c)、属性(a)、背景色(b)に変更します。属性フィールドと背景フィールドはオプションです。
これらのタプルは(paint()への入力用の)カラーマップを作成するため、色は各行の先頭でpaint()に渡され、デフォルトにリセットされます。 たとえば、このコードは、レンダリング時に色付きのつまらないものを含む単純なクリスマスツリーを生成します(デフォルトの色として緑を使用)。
code: pyton
StaticRenderer(images=[r"""
${3,1}*
/ \
/${1}o${2} \
/_ _\
/ \${4}b
/ \
/ ${1}o${2} \
/__ __\
${1}d${2} / ${4}o${2} \
/ \
/ ${4}o ${1}o${2}.\
/___________\
${3}|||
${3}|||
"""])
ユーザインタフェース
Asciimaticsは、インタラクティブなテキストユーザーインターフェイスを作成できるウィジェット(Widget) を提供しています。本質的に、ロジックはとても単純で、一般的なのWebおよびデスクトップGUIフレームワークの概念を採用しています。
テキストUIの基本的な構成要素はウィジェットです。 asciimaticsが提供する標準的なもののセットがありますが、必要に応じてカスタムセットを作成できます。基本セットには、単純なWeb入力フォームとの類似点があります。ボタン、チェックボックスなど。
ウィジェットは画面上に配置し、サイズを変更するたびに再配置する必要があります。 Layoutクラスがこれを処理します。ウィジェットを1つに追加するだけです。
レイアウトを表示する必要があります。これを行うには、それらをフレームに追加する必要があります。このクラスはエフェクトであるため、他のエフェクトと一緒に任意のシーンで使用できます。フレームは、フレームに含まれるレイアウトの境界内に表示される部分を描画します。最終的な結果として、GUIフレームワークのウィンドウのように見え始めます。
重要なイベントが発生したときにトリガーされるように、さまざまなコールバックを設定できます。値の変更、ボタンのクリックなどです。これらを使用して、アプリケーションの処理をトリガーします。例については contact_list.pyサンプルを参照してください。これを実行したスクリーンショットは次のようになります。 https://gyazo.com/04a172eae76684d06741f62853b86156
フレームでは次のキー操作が行えます。
table: キー操作
キー操作 アクション
Tab フレーム内の次のウィジェットに移動
Backtab (shift+tab) フレーム内の前のウィジェットに移動
上カーソル 同じ列の現在のフォーカスの上にあるウィジェットに移動
下カーソル 同じ列の現在のフォーカスの下にあるウィジェットに移動
左カーソル 現在の入力フォーカスがある列の左側にある列の最後のウィジェットに移動
右カーソル 現在の入力フォーカスがある列の右側にある列の最初のウィジェットに移動
スペース/リターン 現在のウィジェットを選択
カーソルキーはレイアウト間を移動しないことに注意してください。 さらに、無効になっているウィジェットに移動することはできません。
テキスト編集ウィジェット内では、カーソルキーのアクションが上書きされ、代わりに、編集可能なテキスト(またはリスト)を期待どおりにナビゲートできるようになります。 さらに、次のキー操作を使用することもできます。
table: ウィジェット内でのキー操作
キー操作 アクション
Home/End 現在の行の開始/終了に移動
Delete カーソルの下の文字を削除
Backspace カーソルの前の文字を削除
Model/Viewの設計
これらのクラスが何か、そして何をするためのものかを説明する前に、これらを最大限に活用するためにどのように組み合わせる必要があるかを理解することが重要です。
Asciimaticsの基礎となるScreen / Scene / Effect の設計は、オブジェクトが定期的に破棄されて再作成されます。特にScreenのサイズが変更された場合です。したがって、データモデルをコードから分離して、画面に表示することが重要になります。
こうした「分離」はしばしば MVCモデル と呼ばれますが、より正確には分離プレゼンテーション(Separated Presentation)です。使用する用語はともかく、概念は簡単です。永続的なデータストレージを処理するためには、別のクラスを使用します。 ContactModelクラス
これはモデルクラスです。単純な連絡先のデータをsqliteインメモリデータベースに保存し、連絡先を操作するための単純な作成/読み取り/更新/削除インターフェイス(CRUDインタフェース)を提供します。実は、サンプルとしてはデータストレージをこれほど重くする必要はありません。データをdict型のリストを保持する簡単なクラスでも十分なことに留意してください。
code: asciimatics_contactmodels.py
class ContactModel(object):
def __init__(self):
# メモリ上にデータベースを作成
self._db = sqlite3.connect(':memory:')
self._db.row_factory = sqlite3.Row
# contactsテーブルを作成
self._db.cursor().execute('''
CREATE TABLE contacts(
id INTEGER PRIMARY KEY,
name TEXT,
phone TEXT,
address TEXT,
email TEXT,
notes TEXT)
''')
self._db.commit()
# 編集時のコンタクトID
self.current_id = None
def add(self, contact):
self._db.cursor().execute('''
INSERT INTO contacts(name, phone, address, email, notes)
VALUES(:name, :phone, :address, :email, :notes)''',
contact)
self._db.commit()
def get_summary(self):
return self._db.cursor().execute(
"SELECT name, id from contacts").fetchall()
def get_contact(self, contact_id):
return self._db.cursor().execute(
"SELECT * from contacts where id=?", str(contact_id)).fetchone()
def get_current_contact(self):
if self.current_id is None:
return {"name": "", "address": "", "phone": "", "email": "", "notes": ""}
else:
return self.get_contact(self.current_id)
def update_current_contact(self, details):
if self.current_id is None:
self.add(details)
else:
self._db.cursor().execute('''
UPDATE contacts SET name=:name, phone=:phone, address=:address,
email=:email, notes=:notes WHERE id=:id''',
details)
self._db.commit()
def delete_contact(self, contact_id):
self._db.cursor().execute('''
DELETE FROM contacts WHERE id=:id''', {"id": contact_id})
self._db.commit()
ListViewクラス
このクラスはメインビューとなります。ContactModel に既知の連絡先のリストを照会し、連絡先を追加/編集/削除するためのいくつかの追加ボタンを備えたリストを表示します。
code: asciimatics_views.py
class ListView(Frame):
def __init__(self, screen, model):
super(ListView, self).__init__(screen,
screen.height * 2 // 3,
screen.width * 2 // 3,
on_load=self._reload_list,
hover_focus=True,
title="Contact List")
# 連絡先データベースにアクセスするモデルを保存
self._model = model
# 連絡先のリストを表示するためのフォームを作成
self._list_view = ListBox(
Widget.FILL_FRAME,
model.get_summary(), name="contacts", on_select=self._on_pick)
self._edit_button = Button("Edit", self._edit)
self._delete_button = Button("Delete", self._delete)
layout = Layout(100, fill_frame=True) self.add_layout(layout)
layout.add_widget(self._list_view)
layout.add_widget(Divider())
self.add_layout(layout2)
layout2.add_widget(Button("Add", self._add), 0)
layout2.add_widget(self._edit_button, 1)
layout2.add_widget(self._delete_button, 2)
layout2.add_widget(Button("Quit", self._quit), 3)
self.fix()
def _on_pick(self):
self._edit_button.disabled = self._list_view.value is None
self._delete_button.disabled = self._list_view.value is None
def _reload_list(self):
self._list_view.options = self._model.get_summary()
self._model.current_id = None
def _add(self):
self._model.current_id = None
raise NextScene("Edit Contact")
def _edit(self):
self.save()
raise NextScene("Edit Contact")
def _delete(self):
self.save()
self._reload_list()
@staticmethod
def _quit():
raise StopApplication("User pressed quit")
ContactViewクラス
これは詳細ビューです。 表示されている現在の連絡先をContactModelクラスに照会し、ユーザーがOKボタンをクリックすると変更されたデータをモデルに書き戻します。
注:ユーザーが連絡先を追加している場合は、まだ連絡先のデータがない場合があります
code: asciimatics_contactview.py
class ContactView(Frame):
def __init__(self, screen, model):
super(ContactView, self).__init__(screen,
screen.height * 2 // 3,
screen.width * 2 // 3,
hover_focus=True,
title="Contact Details")
# 連絡先データベースにアクセスするモデルを保存
self._model = model
# 連絡先のリストを表示するためのフォームを作成
layout = Layout(100, fill_frame=True) self.add_layout(layout)
layout.add_widget(Text("Name:", "name"))
layout.add_widget(Text("Address:", "address"))
layout.add_widget(Text("Phone number:", "phone"))
layout.add_widget(Text("Email address:", "email"))
layout.add_widget(TextBox(5, "Notes:", "notes", as_string=True))
self.add_layout(layout2)
layout2.add_widget(Button("OK", self._ok), 0)
layout2.add_widget(Button("Cancel", self._cancel), 3)
self.fix()
def reset(self):
# 標準のリセットを実行してフォームをクリアしてから、新しいデータを入力
super(ContactView, self).reset()
self.data = self._model.get_current_contact()
def _ok(self):
self.save()
self._model.update_current_contact(self.data)
raise NextScene("Main")
@staticmethod
def _cancel():
raise NextScene("Main")
次に、ユーザーともう少しインタラクティブな処理をしたいと場合を想定しましょう。最初に決定する必要があるのは、ユーザからどのような情報を入手したいのか、そしてそれをどのように実現するかです。
入力できるようにするデータ:ユーザ名
それをどのようにフィールドに分解したいのか:名前と苗字
それらのフィールドをどう表現するのか:テキスト文字列
これらを設計できると、使用するウィジェットを決定できます。 標準的なものは次のとおりです。
table: ウィジェット
ウィジェットタイプ 説明
Button アクションボタン 例:OK/CANCEL など
CheckBox 単純な Yes/No のチェックボックス
DatePicker ポップアップリストを使用して日付選択の1行ウィジェット
Divider ウィジェット間のスペーサー
DropdownList ユーザーがひとつ値を選択できるリストをポップアップする1行ウィジェット
FileBrowser ローカルファイルシステムを一覧表示するための複数行ウィジェット
Label 関連するウィジェットグループのラベル
ListBox ユーザーが1つの値を選択できる可能なオプションのリスト
MultiColumnListBox ListBoxに似ているが、表形式のデータを表示するためのもの
RadioButtons ラジオボタンのリスト ユーザーは候補リストから1つの値を選択する
Text 編集可能なテキストの1行
TextBox 編集可能なテキストの複数行
TimePicker ポップアップリストを使用して時間を選択するための1行ウィジェット
VerticalDivider 垂直線の仕切り レイアウトの列の間に視覚的なマーカーを提供する
注:Textウィジェットの hide_char オプションを使用すると、
パスワードなどの機密データを非表示にすることができます。
Asciimaticsは、ほんの少しの手間で、これらを自動的に整えます。 すべきことはフィールドに必要な列の数とどのフィールドがどの列に配置するかを決めることです。 Layoutクラスのインスタンスを作成し、表示するフレームに関連付けます。より複雑な画面構成が必要な場合は、Layoutオブジェクトは複数作成することになります。
次のコードは、80x20文字のフレームが作成され、それぞれが20列幅の4列が定義されます。
code: python
frame = Frame(screen, 80, 20, has_border=False)
frame.add_layout(layout)
レイアウトができたら、関連する列にウィジェットを追加できます。
次のコードは、最初と最後の列にボタンが追加されます。
code: python
ayout.add_widget(Button("OK", self._ok), 0)
layout.add_widget(Button("Cancel", self._cancel), 3)
すべての入力フィールドに標準のラベルを付けたい場合は、このままも構いません。 asciimaticsは、同じ列のすべてのフィールドでラベルの大きさを決定し、それらすべてをインデントして、より見た目に美しいレイアウトを作成します。
次のコードは、各フィールドのラベルが1つの列で提供され、すべてのフィールドが同じ深さまでインデントされます。
code: python
frame.add_layout(layout)
layout.add_widget(Text("Name:", "name"))
layout.add_widget(Text("Address:", "address"))
layout.add_widget(Text("Phone number:", "phone"))
layout.add_widget(Text("Email address:", "email"))
layout.add_widget(TextBox(5, "Notes:", "notes", as_string=True))
ラベルを直接制御したい場合は、Labelウィジェットを使用して、レイアウト内の任意の場所にラベルを配置したり、テキストの位置合わせ(左、中央、または右)を制御したりできます。
UIに静的テキストが必要なだけであれば、Labelウィジェットを使用するだけですみます。事前にフォーマットされた複数行のステータスバーを配置するような、もう少し複雑な構成が必要な場合は、TextBoxウィジェットを使用します。
場合によっては、ウィジェットのさまざまなブロックに対して異なる配置が必要になることがあります。 このケースを処理するために、1つのフレームで複数のレイアウトを使用することができます。
次のコードは、フレームの上部でデータを入力でき、下部に結果のリストを入力できる検索ページとなります。
code: python
frame.add_layout(layout1)
layout1.add_widget(Text(label="Search:", name="search_string"))
frame.add_layout(layout2)
layout1.add_widget(TextBox(Widget.FILL_FRAME, name="results"))
ウィジェットの無効化
disableプロパティを設定すると、ウィジェットを無効にできます。 これがTrueの場合、asciimaticsはカラーパレットの disabledエントリを使用してウィジェットを再描画し、ユーザーがウィジェットを選択または編集できないようにします。
ただし、ウィジェットをプログラムで変更することは可能です。 たとえば、無効になっているウィジェットの値を変更することはできます。
これは、非対話型データ(ステータスバーなど)をUIに取り込むための推奨される方法です。 無効にした色がUIの誤った選択である場合は、カスタムウィジェットの色で説明されているようにオーバーライドできます。 このようなウィジェットの例については、top.pyサンプルを参照してください。 レイアウトについての補足説明
より複雑な構成にする必要がある場合は、複数のレイアウトを使用できます。 Asciimaticsは、次のロジックを使用してウィジェットの場所を決定します。
フレームは1つ以上のレイアウトを所有しています。 レイアウトは、表示時に上下に重なります。つまり、フレームの最初のレイアウトが2番目のレイアウトの上にあります。
各レイアウトは、フレーム幅全体の比率として列を定義することにより、いくつかの水平方向の制約を定義します。
ウィジェットには、ウィジェットを所有するレイアウト内の列が割り当てられます。
次に、レイアウトは正確なサイズと場所を決定して、上記の制約に従って各ウィジェットが表示スペースに最適になるようにします。
code: display
+------------------------------------------------------------------------+
|Screen..................................................................|
|........................................................................|
|...+----------------------------------------------------------------+...|
|...|Frame |...|
|...|+--------------------------------------------------------------+|...|
|...||Layout 1 ||...|
|...|+--------------------------------------------------------------+|...|
|...|+------------------------------+-------------------------------+|...|
|...||Layout 2 | ||...|
|...|| - Column 1 | - Column 2 ||...|
|...|+------------------------------+-------------------------------+|...|
|...|+-------------+---------------------------------+--------------+|...|
|...||Layout 3 | < Widget 1 > | ||...|
|...|| | ... | ||...|
|...|| | < Widget N > | ||...|
|...|+-------------+---------------------------------+--------------+|...|
|...+----------------------------------------------------------------+...|
|........................................................................|
+------------------------------------------------------------------------+
これは、3つのレイアウトを持つ単一のフレームで構成されます。
1つ目は単一の全幅列
2つ目は2つの50%幅列
3つ目は相対サイズ25:50:25の3つの列
最後の列には、実際には2番目の列にいくつかのウィジェットが含まれています。
余白を埋める
UIの基本的な行と列を並べ替えたあとは、スペースを配置するようにします。最も単純なレベルでは、前述のDividerウィジェットを使用して、追加の垂直スペースを作成したり、視覚的なセクション区切りを挿入したりできます。
複雑さを増すと、現在の画面のサイズに基づいて、フレームにさまざまなサイズを選択できます。画面のサイズが変更されるとフレームが再作成されるため、適切な場所に配置されます。
最後に、残りのスペースを埋めるためにオブジェクトを使用するようにasciimaticsに指示することもできます。これにより、ヘッダーやフッターが固定されている top.py などのアプリケーションで見られるようなUIが可能になりますが、表示されるデータを含むさまざまなサイズのパーツが可能になります。 これは2つの方法で実現できます。
構築時に fill_frame=True を使用して、フレーム内の残りのスペースを埋めるようにレイアウトに指示できます。
構築時にWidget.FILL_FRAMEの高さを使用して、フレーム内の残りのスペースを埋めるように一部のウィジェットに指示できます。
これらの2つの方法を組み合わせて、フレームを埋めるためのレイアウトと、このレイアウトを埋めるためのウィジェットを指示できます。サンプルコード contact_list.py の ListViewクラスを参照してください。 注意:
フレームを埋めるレイアウトやウィジェットは1つしか持てないことに注意してください。
複数設定しようとすると拒否されます。
フルスクリーンフレーム
デフォルトでは、asciimaticsは、複数のフレームを1つのシーンに配置していることを前提としているため、このタイプのUIを最適化するためのデフォルト(境界線など)を提供します。 ただし、一部のUIには、単一のフルスクリーンフレームのみが必要です。 これは、フレームを画面の全幅と高さで宣言してから、has_border=False を指定することで簡単に実現できます。
大きなフレーム
フォームが非常に大きい場合は、大きすぎて標準の画面に収まらないことがあります。 これは問題ではありません。 ウィジェットをレイアウトに追加し続けることができ、asciimaticsはコンテンツを使用可能なスペースに自動的にクリップし、必要に応じてコンテンツをスクロールします。
これを行う場合は、フレームに has_border=True を設定して、ユーザーが提供されているスクロールバーを使用してフォーム内を移動できるようにすることをお勧めします。
カラースキーム
ウィジェットの色は、ウィジェットを含むフレームのパレットプロパティによって決定されます。 必要に応じて、フレームごとに異なるパレットを使用することもできますが、ユーザーはより統一性のあるUIを好む場合があります。
パレットは、ウィジェットコンポーネントをカラータプルにマップするための単純なdict型データです。 カラータプルは、単に前景色、属性、および背景色です。
code: python
(Screen.COLOUR_GREEN, Screen.A_BOLD, Screen.COLOUR_BLUE)
次の表に、パレットに必要なキーを示します。
table: カラーパレット
Key 使用方法
“background” フレームの背景
“borders” フレーム枠とDividerウィジェット
“button” Buttonウィジェット
“control” CheckBoxウィジェットとRadiButtonsウィジェット
“disabled” すべての無効ウィジェット
“edit_text” TextウィジェットとTextBoxウィジェット
“field” Checkbox、RadioButtons、Listboxのオプションの値
“focus_button” 入力フォーカスがある Buttonウィジェット
“focus_control” 入力フォーカスがある Checkboxe、RadioButtonsウィジェット
“focus_edit_text” 入力フォーカスがあるText、TextBoxウィジェット
“focus_field” 入力フォーカスがあるフィールド
“invalid” 無効な値を含むウィジェット
“label” ウィジェットのラベル
“scroll” フレームのスクロールバー
“selected_control” Checkbox、 RadioButtons ウィジェットの選択中の項目
“selected_field” 選択中のフィールド
“selected_focus_control” 入力フォーカスがあるCheckbox、RadioButtons ウィジェットの選択中の項目
“selected_focus_field” 入力フォーカスがある選択中のフィールド
“title” フレームタイトル
すべてのウィジェットのデフォルトの配色に加えて、asciimaticsは、set_theme()を使用してウィジェットに使用できる他のいくつかの定義済みの配色(テーマ:Theme)を提供します。 これらのテーマは以下のとおりです。
table: テーマ
テーマ名 説明
“monochrome” シンプルな白黒の配色
“green” クラシカルなグリーンターミナル
“bright” 黒の背景、緑と黄色のスキーム
“tlj256” 黒、白、赤の影 256色表示可能な端末のみ
ウィジェットの色をカスタマイズ
場合によっては、フレーム全体の単一のパレットでは不十分です。 よりきめ細かい色付けのアプローチが必要な場合は、ウィジェットのcustom_colourを設定することで、ウィジェットの色をカスタマイズできます。 このプロパティの唯一の制約は、所有しているフレームのパレット内のキーの1つの値でなければならないということです。
インラインで色を変更
ほとんどのUIでは、ここまでの設定で十分ですが、一部のウィジェットの値内の一部のテキストの色を変更できると便利な場合があります。 asciimatics はTextBoxで構文の強調表示ができます。これをサポートするウィジェットのParserオブジェクトを使用すると、こうした要求に対応することができます。
追加の制御コードや特定の文字を異なる方法で強調表示する指示を理解するパーサーを渡すことにより、文字ごとに色を制御できます。 asciimaticsは、このための2つのパーサーを提供しています。これらのパーサーは、レンダラーで使用される ${c,a,b}形式やほとんどの端末で使用できるANSI標準の端末エスケープコードを処理できます。 必要に応じてパーサーに制御コードを含む値を与えるだけで色を変更できます。
ウィジェット内の値を設定する
ここまでの情報で、基本的なユーザーインターフェースが稼働できるはずですが、フォームに既知の値を事前入力するために、各ウィジェットの値をどのように設定するかについて考えてみましょう。
これを処理する方法は2つあります。
valueプロパティを使用して、各ウィジェットに直接値を設定できます。
dataプロパティで設定することで、Frameウィジェット内のすべての値を設定できます。
これは、各ウィジェットのnameプロパティをキーとして使用する単純なdict型のデータです。
各ウィジェットのすべてのデータにアクセスするための対称的なソリューションが提供されているため、dataプロパティで設定することが推奨されます。
ウィジェット内の値を取得する
いくつかのウィジェットを含むフレームがあり、ユーザーがそれらを入力しているとき、どのようにしてそれらが入力したものを見つけるのでしょうか? このための2つの基本的な方法があります。
valueプロパティを使用して、各ウィジェットに直接クエリを実行できます。 これにより、フレームがアクティブであるかどうかを問わずいつでも、ユーザーが入力した現在の値が返されます。Buttonウィジェットなどのように値がないウィジェットの場合は None になる可能性があることに留意してください。
dataプロパティを見ることで、Frameウィジェット内のすべての値を取得できます。 これにより、キーにウィジェット名プロパティを与えて、Frameウィジェット内のすべてのウィジェットの値がdict型データとして返されます。 データは単なるキャッシュで、save()を呼び出したときにのみ更新されるため、アクセスする前にこのメソッドを呼び出してキャッシュを更新する必要があることに注意してください。
code: python
# Form definition
frame.add_layout(layout)
layout.add_widget(Text("Name:", "name"))
layout.add_widget(Text("Address:", "address"))
layout.add_widget(TextBox(5, "Notes:", "notes", as_string=True))
# ユーザーが入力した後のサンプルframe.data
{
"name": "Peter",
"address": "Somewhere on earth",
"notes": "Some multi-line\ntext from the user."
}
データの検証
テキスト入力では、メールアドレスなどのように、ユーザーが正しいものを入力したことを確認するために検証が必要になる場合があります。 こうしたときは、Textウィジェットにvalidatorパラメータを追加することで簡単に対応できます。
validator パラメーターには、正規表現文字列や現在のウィジェットをパラメータとしてひとつ受け取る関数のいずれかを与えます。asciimaticsはこれを使用して、ウィジェットに有効なデータが含まれているかどうかを判断します。 この情報は2か所で使用されます。
フレームが再描画されるたびに状態をチェックし、invalidカラーパレットを選択を使用して無invalidフラグを立てます。
save() にvalidate=True を与えて呼び出すと、asciimaticsはすべてのフィールドをチェックし、無効なデータが見つかった場合は InvalidFields 例外を発生させます。
入力フォーカス
カラーパレットの説明でも述べたように、asciimaticsには入力フォーカスの概念があります。 これは、キーボードからの入力を受け取るウィジェットです。 デフォルトのパレットを使用していると仮定すると、入力フォーカスのあるウィジェットが強調表示されます。 フォーカスは、カーソルキー、タブ/バックタブ、またはマウスを使用して移動することができます。
マウスがフォーカスに影響を与える正確な方法は、端末ソフトウェアの機能とフレームの設定の組み合わせによって異なります。 少なくとも、ウィジェットのクリックは常に機能します。 端末ソフトウェアがマウス移動イベントのレポートをサポートしている場合、hover_focus=True を指定することで、マウスポインターでウィジェットにカーソルを合わせるだけで、フォーカスが移動します。
モーダルフレーム
フレームを作成するときに、is_modalパラメーターを使用してフレームをモーダルにするかどうかを指定できます。 モーダルフレームでは、入力をScene内の他のエフェクトにフィルタリングすることはできません。そのため、他のすべてのエフェクトの上にある場合、そのエフェクトだけにしかユーザーは入力できないことになります。
これは通常、確認が必要なユーザーへの通知に使用されますが、これに限定されているわけではありません。
グローバルキー処理
フォーカスを切り替えるためのマウスコントロールに加えて、フォームをナビゲートするためのグローバルイベントハンドラーを設定することもできます。 これは、キーボードショートカットに役立ちます。例えば、Ctrl + Q を押下されるとプログラムを終了するような場合です。
このハンドラーを設定するためには、play()メソッドのScreenオブジェクトにハンドラーを渡す必要があります。
code: python
# グローバルキーのイベントハンドラー
def global_shortcuts(event):
if isinstance(event, KeyboardEvent):
c = event.key_code
# ctrl+q か ctrl+x で終了
if c in (17, 24):
raise StopApplication("User terminated app")
# この設定をScreenオブジェクトに渡す
screen.play(scenes, unhandled_input=global_shortcuts)
注意:
グローバルハンドラーは、フォーカスがイベントを処理しない場合にのみ呼び出されます。
TextBoxウィジェットなどの一部のウィジェットはテキストを入力を取得するため、
このハンドラーに到達するキーは制御コードのみです。
フォーカスされているウィジェットのタイプや、フレームがモーダルであるかどうかによっては、
他のキーが到達することがあります。
デフォルトでは、フレームを含むシーンを再生している場合、グローバルハンドラーは何もしません。 それ以外の場合は、次のシーンにスキップする(スペースまたはEnter)、またはプログラムを終了する(QまたはX)ためのトップレベルロジックが含まれています。
Ctrl + CおよびCtrl + Zの処理
一般的に、Ctrl + C / Ctrl + Zはアプリケーションの強制終了や中断するためのキー操作です。
しかし、最近のUIの多くは、これらのキー操作を別の目的にために使用できるようにしているものもあります。Pythonの問題は、Ctrl + Cのキー操作で、KeyboardInterrupt例外を発生させてしまうことです。また、Linux系プラットフォームではCtrl + Z はオペレーティングシステムがプロセスを一時停止させてしまいます。
これを防ぎ、別の目的で Ctrl + C / Ctrl + Z を使用する場合は、低レベルのシグナルをキャッチして、これらの割り込みが生成されないようにasciimaticsに指示することができます。
これは、Screenを作成するときにwrapper()にcatch_interrupt=True を与えて呼び出すことで実装することができます。
code: python
Screen.wrapper(demo, catch_interrupt=False)
Ctrl + Sの処理
端末が実際には別のマシンにモデムで接続されていた時代には、端末の処理が追いつくのに時間が必要であることを通知できる必要がありました。 これは、ソフトウェアフロー制御を使用して行われ、Ctrl + S / Ctrl + Q 制御コードを使用して、テキストの送信を停止/再開するようにコンピューターに指示しました。
最近では、実際には必要ありませんが、ほとんどの端末でサポートされている機能です。 一部のシステムでは、これをオフにしてCtrl + Sにアクセスできるようにすることができますが、すべてのシステムで可能というわけではありません。 対応方法の詳細については、Ctrl+S does not work を参照してください。 制御の流れ
ここまでの説明で、いくつかのフレームを備えたプログラムが作成することができ、ユーザーがそれらのいずれかに入力したデータを抽出することができます。 しかし、ユーザがフレーム間を移動するかをどうやって知るのでしょうか? その答えはコールバックと例外です。
コールバック
コールバックは、関連するイベントが発生したときに呼び出される別の関数に渡す関数のことです。 一般的に、コールバック関数の名前は、onで始まり、ユーザーからの重要な入力操作に対応するアクションがつけられます。例: on_click()
アプリケーションを作成するときは、処理をトリガーして適切なコールバックを作成するために使用するイベントを決定するだけです。 最も一般的なパターンは、ボタンを使用してon_click()コールバックを定義します。
さらに、ウィジェットの値が変更されたときにトリガーできる他のイベントがあります。 これらは、別のウィジェットの現在の値に基づいてボタンを有効/無効にするなどの動的効果を提供するために使用できます。
例外
asciimaticsは例外を使用して、アニメーションエンジンに新しいシーンに移動するか、全体を停止するように指示します
処理する。 その他の例外はキャッチされないため、通常どおり使用できます。
新しい例外の詳細は次のとおりです。
StopApplication:アニメーションエンジンを停止し、画面を呼び出した関数に制御を戻します。
NextScene:アニメーションエンジンに新しいシーンに移動するように指示します。 移動すべきシーンは、例外に渡された名前によって決まります。 何も指定されていない場合、エンジンは単に次に使用可能なシーンをラウンドロビンロビンで選ばれます。
このロジックでは、構築時にそれぞれシーンに一意の名前を付ける必要があることに注意してください。
code: python
# Sceneリスト...
scenes = [
]
screen.play(scenes)
# このコードを使用して、いつでも最初のシーンに戻ることができます...
raise NextScene("Main")
データ処理
ここまでの情報で、完全に機能するUIに必要なものがすべて揃っているはずです。ただし、asciimaticsにはデータを渡すためのクラスが用意されていないため、すべてのコンポーネントパーツにデータを渡す方法が明確ではありません。これは、特定の実装に縛られたくないという理由からです。
Model/Viewの設計より前の説明を振り返ってください。モデルは自由に好きなクラスに定義することができます
モデルクラスを定義して任意の状態を保存し、UI(別名ビュー)から必要に応じてそれにアクセスするための適切なAPIを提供します。
EffectやFrameに基づいた独自のビューを定義したUIを設計し、モデルへの参照を保存します
モデルへの保存された参照を使用して、ビューのコールバックまたはメソッド内で必要に応じて更新処理します。
動的なシーン
シーンを動的に変更したり、再構築する方法について説明しましょう。
まずは何を達成したいかを決める必要があります。 基本的な選択肢は次のとおりです。
同じ画面にいくつかの追加のフレームが必要な場合
例: ポップアップウィンドウ
既存のクラスを使用するだけです
既存のフレームの外に他のコンテンツを描画できるようにしたい場合
おそらく他のエフェクトを使用することをお勧めします。
フレーム内に何かを追加できるようにしたい場合
ほぼ確実に、その新しいコンテンツ用のカスタムウィジェットを作成する必要があります。
このセクションの残りの部分では、これらの方法についてもう少し詳しく説明します。
他の効果を追加
フレームは単なる別のエフェクトであるため、シーン内の他のエフェクトと組み合わせることができます。
次のコードは、アニメーション化されたジュリア集合のアニメーション効果の上に簡単な入力フォームが配置されます。
code: python
scenes = []
effects = [
Julia(screen),
InputFormFrame(screen)
]
scenes.append(Scene(effects, -1))
screen.play(scenes)
このときの順序は重要です。リストの下部にあるエフェクトは、ScrenのZオーダー の上部にあるため、Zオーダーの下位のエフェクト(つまり、リストの前のエフェクト)よりも優先して表示されます。 補足説明:
Zオーダーは、前面と背面と考える理解しやすいでしょう。
これを使用する最も可能性の高い理由は、背景効果を使用して、フレームの背後にある画面全体の背景色を設定することです。この例については、forms.py サンプルを参照してください。 PopUpDialog
おなじように、いつでもPopUpDialogをSceneに追加できます。これらは、単一のテキストメッセージと、ダイアログの作成時に定義できる一連のボタンで構成されています。
画面のサイズが変更されたときにオブジェクトを再構築する必要がある方法に制限があるため、これらは確認またはエラーの場合の単純なものに限定する必要があります。
例: ”XXXします! 本当によろしいですか?"
制限の詳細については、状態の復元に関するセクションを参照してください。
ポップアップメニュー
同様に、PopupMenuをシーンに追加することもできます。これらを使用すると、ユーザーが1つのエントリのみを選択する(クリックするか、フォーカスを移動してEnterキーを押す)か、リスト全体を閉じる(エスケープキーを押すか、メニューの外側をクリックする)必要がある単純な一時リストを作成できます。
それらは一時的なものであるため、画面のサイズ変更されると消失します。
画面のサイズ変更
すべてのサンプルコード にある標準のアプリケーションメインラインロジックに従う場合、ユーザーが端末のサイズを変更するたびに、アプリケーションはすべてのエフェクトとウィジェットのサイズを変更する必要があります。 このためには、新しいScreenオブジェクトを取得してから、そのScreenオブジェクトを使用するために新しいオブジェクトのセットを再構築する必要があります。
ちょっとややこしく聞こえますね? このことが、プレゼンテーションをアプリケーションロジックから分離することをお勧めする理由です。 それを正しく分離できると、実際には、最初にシーンを作成する前とまったく同じ初期化パスを通過することを意味することになります。
ただし、いくつかの落とし穴があります。
まず、端末のサイズが変更されたときに、asciimaticsが終了し、新しい画面が再作成されることを確認する必要があります。 このためには、ほとんどのすべてのサンプルコード に含まれている、次のボイラープレートコード を使用して行います。 code: python
def main(screen, scene):
# Scene をここに定義
scenes = ...
# アプリケーションを実行
screen.play(scenes, stop_on_resize=True, start_scene=scene)
last_scene = None
while True:
try:
sys.exit(0)
except ResizeScreenError as e:
last_scene = e.scene
これで、画面のサイズが変更されたときに、すべてのUIがどのように表示されるかを決定できます。
サイズ変更時に再生していたシーンから再開します。
状態の復元
ビューを再現するだけでは十分ではありません。アプリケーション内の状態を復元したことを確認する必要があります。動的効果が追加されたり、新しいシーンの内部状態に古いシーンの内部状態からコピーします。asciimaticsは、こうした目的のための標準インターフェイスclone()メソッドを提供します。
実行中のシーンのサイズが変更され、開始シーンとして画面に戻されると、新しいシーンはクローンメソッドでコピーされた古いすべてのエフェクトを実行します。見つかった場合は、2つのパラメーターを使用して呼び出します。クローンされた効果を持つ新しい画面と新しいシーンです。これにより、新しいエフェクトの再作成方法を完全に制御できます。 asciimaticsは、デフォルトで2つの方法でこのインターフェースを使用します。
新しいシーンでデータが確実に復元されるようにする
動的に追加されたPopUpDialogオブジェクトを新しいシーンにクローンする
この処理をオーバーライドして、独自のカスタムクローン作成ロジックを定義することもできます。 APIの正式な定義は次のように定義されています。
code: python
def clone(self, screen, scene):
"""
このエフェクトのクローンを新しい画面に作成する
:param screen: クローンを作成する新しいScreenオブジェクト
:param scene: クローンを作成する新しいSceneオブジェクト
"""
CPU使用率の削減
本格的なアニメーションシーンほど頻繁に更新する必要がないことは、テキストUIの性質です。したがって、asciimaticsは、フレームのみが画面に表示されている場合にリフレッシュレートを最適化します。
ただし、アニメーションの更新を要求しないことで、アニメーションの必要性をさらに減らすことができるウィジェットがいくつかあります(カーソルの点滅など)。これがアプリケーションの問題である場合は、フレームを作成するときに reduce_cpu=True を指定できます。
カスタムウィジェット
独自のウィジェットを開発するには、Widgetクラスを継承した新しいクラスを定義する必要があります。次に、次の機能を実装する必要があります。
reset():これは、ウィジェットの状態をリセットする必要がある場所です。所有しているフレームが初期化されるたびに呼び出されます。これは、最初に表示されたとき、ユーザーが新しいシーンに移動したとき、または画面のサイズが変更されたときです。
update():これは、ウィジェットを描画するためのロジックを配置する必要がある場所です。 asciimaticsが画面を再描画する必要があるたびに呼び出されます(したがって、常にウィジェット全体を描画する必要があります)。
process_event():これは、マウスとキーボードのイベントを処理するためのコードを配置する場所です。
value():これはウィジェットの現在の値を返す必要があります。
required_height():これは、ウィジェットに必要な最小の高さを返します。所有するレイアウトがウィジェットのサイズと場所を決定するために使用します。
これらがすべて定義されると、このパッケージで提供される標準ウィジェットと同様に、新しいカスタムウィジェットをレイアウトに追加できるようになります。